1
|
|
|
/* http://github.com/mindmup/bootstrap-wysiwyg */ |
2
|
|
|
/*global jQuery, $, FileReader*/ |
3
|
|
|
/*jslint browser:true*/ |
4
|
|
|
(function ($) { |
5
|
|
|
'use strict'; |
6
|
|
|
var readFileIntoDataUrl = function (fileInfo) { |
7
|
|
|
var loader = $.Deferred(), |
8
|
|
|
fReader = new FileReader(); |
9
|
|
|
fReader.onload = function (e) { |
10
|
|
|
loader.resolve(e.target.result); |
11
|
|
|
}; |
12
|
|
|
fReader.onerror = loader.reject; |
13
|
|
|
fReader.onprogress = loader.notify; |
14
|
|
|
fReader.readAsDataURL(fileInfo); |
15
|
|
|
return loader.promise(); |
16
|
|
|
}; |
17
|
|
|
$.fn.cleanHtml = function () { |
18
|
|
|
var html = $(this).html(); |
19
|
|
|
return html && html.replace(/(<br>|\s|<div><br><\/div>| )*$/, ''); |
20
|
|
|
}; |
21
|
|
|
$.fn.wysiwyg = function (userOptions) { |
22
|
|
|
var editor = this, |
23
|
|
|
selectedRange, |
24
|
|
|
options, |
25
|
|
|
toolbarBtnSelector, |
26
|
|
|
updateToolbar = function () { |
27
|
|
|
if (options.AktifToolbarClass) { |
28
|
|
|
$(options.toolbarSelector).find(toolbarBtnSelector).each(function () { |
29
|
|
|
var command = $(this).data(options.commandRole); |
30
|
|
|
if (document.queryCommandState(command)) { |
31
|
|
|
$(this).addClass(options.AktifToolbarClass); |
32
|
|
|
} else { |
33
|
|
|
$(this).removeClass(options.AktifToolbarClass); |
34
|
|
|
} |
35
|
|
|
}); |
36
|
|
|
} |
37
|
|
|
}, |
38
|
|
|
execCommand = function (commandWithArgs, valueArg) { |
39
|
|
|
var commandArr = commandWithArgs.split(' '), |
40
|
|
|
command = commandArr.shift(), |
41
|
|
|
args = commandArr.join(' ') + (valueArg || ''); |
42
|
|
|
document.execCommand(command, 0, args); |
43
|
|
|
updateToolbar(); |
44
|
|
|
}, |
45
|
|
|
bindHotkeys = function (hotKeys) { |
46
|
|
|
$.each(hotKeys, function (hotkey, command) { |
47
|
|
|
editor.keydown(hotkey, function (e) { |
48
|
|
|
if (editor.attr('contenteditable') && editor.is(':visible')) { |
49
|
|
|
e.preventDefault(); |
50
|
|
|
e.stopPropagation(); |
51
|
|
|
execCommand(command); |
52
|
|
|
} |
53
|
|
|
}).keyup(hotkey, function (e) { |
54
|
|
|
if (editor.attr('contenteditable') && editor.is(':visible')) { |
55
|
|
|
e.preventDefault(); |
56
|
|
|
e.stopPropagation(); |
57
|
|
|
} |
58
|
|
|
}); |
59
|
|
|
}); |
60
|
|
|
}, |
61
|
|
|
getCurrentRange = function () { |
62
|
|
|
var sel = window.getSelection(); |
63
|
|
|
if (sel.getRangeAt && sel.rangeCount) { |
|
|
|
|
64
|
|
|
return sel.getRangeAt(0); |
65
|
|
|
} |
66
|
|
|
}, |
67
|
|
|
saveSelection = function () { |
68
|
|
|
selectedRange = getCurrentRange(); |
69
|
|
|
}, |
70
|
|
|
restoreSelection = function () { |
71
|
|
|
var selection = window.getSelection(); |
72
|
|
|
if (selectedRange) { |
73
|
|
|
try { |
74
|
|
|
selection.removeAllRanges(); |
75
|
|
|
} catch (ex) { |
76
|
|
|
document.body.createTextRange().select(); |
77
|
|
|
document.selection.empty(); |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
selection.addRange(selectedRange); |
81
|
|
|
} |
82
|
|
|
}, |
83
|
|
|
insertFiles = function (files) { |
84
|
|
|
editor.focus(); |
85
|
|
|
$.each(files, function (idx, fileInfo) { |
86
|
|
|
if (/^image\//.test(fileInfo.type)) { |
87
|
|
|
$.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) { |
88
|
|
|
execCommand('insertimage', dataUrl); |
89
|
|
|
}).fail(function (e) { |
90
|
|
|
options.fileUploadError("file-reader", e); |
91
|
|
|
}); |
92
|
|
|
} else { |
93
|
|
|
options.fileUploadError("unsupported-file-type", fileInfo.type); |
94
|
|
|
} |
95
|
|
|
}); |
96
|
|
|
}, |
97
|
|
|
markSelection = function (input, color) { |
98
|
|
|
restoreSelection(); |
99
|
|
|
if (document.queryCommandSupported('hiliteColor')) { |
100
|
|
|
document.execCommand('hiliteColor', 0, color || 'transparent'); |
101
|
|
|
} |
102
|
|
|
saveSelection(); |
103
|
|
|
input.data(options.selectionMarker, color); |
104
|
|
|
}, |
105
|
|
|
bindToolbar = function (toolbar, options) { |
106
|
|
|
toolbar.find(toolbarBtnSelector).click(function () { |
107
|
|
|
restoreSelection(); |
108
|
|
|
editor.focus(); |
109
|
|
|
execCommand($(this).data(options.commandRole)); |
110
|
|
|
saveSelection(); |
111
|
|
|
}); |
112
|
|
|
toolbar.find('[data-toggle=dropdown]').click(restoreSelection); |
113
|
|
|
|
114
|
|
|
toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () { |
115
|
|
|
var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */ |
116
|
|
|
this.value = ''; |
117
|
|
|
restoreSelection(); |
118
|
|
|
if (newValue) { |
119
|
|
|
editor.focus(); |
120
|
|
|
execCommand($(this).data(options.commandRole), newValue); |
121
|
|
|
} |
122
|
|
|
saveSelection(); |
123
|
|
|
}).on('focus', function () { |
124
|
|
|
var input = $(this); |
125
|
|
|
if (!input.data(options.selectionMarker)) { |
126
|
|
|
markSelection(input, options.selectionColor); |
127
|
|
|
input.focus(); |
128
|
|
|
} |
129
|
|
|
}).on('blur', function () { |
130
|
|
|
var input = $(this); |
131
|
|
|
if (input.data(options.selectionMarker)) { |
132
|
|
|
markSelection(input, false); |
133
|
|
|
} |
134
|
|
|
}); |
135
|
|
|
toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () { |
136
|
|
|
restoreSelection(); |
137
|
|
|
if (this.type === 'file' && this.files && this.files.length > 0) { |
138
|
|
|
insertFiles(this.files); |
139
|
|
|
} |
140
|
|
|
saveSelection(); |
141
|
|
|
this.value = ''; |
142
|
|
|
}); |
143
|
|
|
}, |
144
|
|
|
initFileDrops = function () { |
145
|
|
|
editor.on('dragenter dragover', false) |
146
|
|
|
.on('drop', function (e) { |
147
|
|
|
var dataTransfer = e.originalEvent.dataTransfer; |
148
|
|
|
e.stopPropagation(); |
149
|
|
|
e.preventDefault(); |
150
|
|
|
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { |
151
|
|
|
insertFiles(dataTransfer.files); |
152
|
|
|
} |
153
|
|
|
}); |
154
|
|
|
}; |
155
|
|
|
options = $.extend({}, $.fn.wysiwyg.defaults, userOptions); |
156
|
|
|
toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']'; |
157
|
|
|
bindHotkeys(options.hotKeys); |
158
|
|
|
if (options.dragAndDropImages) { |
159
|
|
|
initFileDrops(); |
160
|
|
|
} |
161
|
|
|
bindToolbar($(options.toolbarSelector), options); |
162
|
|
|
editor.attr('contenteditable', true) |
163
|
|
|
.on('mouseup keyup mouseout', function () { |
164
|
|
|
saveSelection(); |
165
|
|
|
updateToolbar(); |
166
|
|
|
}); |
167
|
|
|
$(window).bind('touchend', function (e) { |
168
|
|
|
var isInside = (editor.is(e.target) || editor.has(e.target).length > 0), |
169
|
|
|
currentRange = getCurrentRange(), |
170
|
|
|
clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset); |
171
|
|
|
if (!clear || isInside) { |
172
|
|
|
saveSelection(); |
173
|
|
|
updateToolbar(); |
174
|
|
|
} |
175
|
|
|
}); |
176
|
|
|
return this; |
177
|
|
|
}; |
178
|
|
|
$.fn.wysiwyg.defaults = { |
179
|
|
|
hotKeys: { |
180
|
|
|
'ctrl+b meta+b': 'bold', |
181
|
|
|
'ctrl+i meta+i': 'italic', |
182
|
|
|
'ctrl+u meta+u': 'underline', |
183
|
|
|
'ctrl+z meta+z': 'undo', |
184
|
|
|
'ctrl+y meta+y meta+shift+z': 'redo', |
185
|
|
|
'ctrl+l meta+l': 'justifyleft', |
186
|
|
|
'ctrl+r meta+r': 'justifyright', |
187
|
|
|
'ctrl+e meta+e': 'justifycenter', |
188
|
|
|
'ctrl+j meta+j': 'justifyfull', |
189
|
|
|
'shift+tab': 'outdent', |
190
|
|
|
'tab': 'indent' |
191
|
|
|
}, |
192
|
|
|
toolbarSelector: '[data-role=editor-toolbar]', |
193
|
|
|
commandRole: 'edit', |
194
|
|
|
AktifToolbarClass: 'btn-info', |
195
|
|
|
selectionMarker: 'edit-focus-marker', |
196
|
|
|
selectionColor: 'darkgrey', |
197
|
|
|
dragAndDropImages: true, |
198
|
|
|
fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); } |
|
|
|
|
199
|
|
|
}; |
200
|
|
|
}(window.jQuery)); |
201
|
|
|
|
This check looks for functions where a
return
statement is found in some execution paths, but not in all.Consider this little piece of code
The function
isBig
will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly returnundefined
.This behaviour may not be what you had intended. In any case, you can add a
return undefined
to the other execution path to make the return value explicit.